/** @file 
 * Funzione principale.
 * @mainpage The City Runner
 * Rungame dove il protagonista deve saltare e evitare gli oggetti che gli arriveranno incontro per raggiungere la sua amica. 
 * È stata utilizzata la libreria grafica Allegro 5. 
 * Si rimanda alla documentazione della funzione ::main per ulteriori dettagli.
 */

#include <iostream>
#include <cstdio>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_font.h>
#include "strutt_dati.h"
#include "aggiorna.h"
#include "inizz_config.h"
using namespace std;

/** Definizione utile per il debug */
#ifdef DEBUG_MODE
#define DBG(a) {a;}
#else 
#define DBG(a)
#endif

/** Vengono definite le strutture principali e inizializzate. 
 * È presente un ciclo per la visualizzazione del menu e delle istruzioni e un altro per il gioco vero e proprio. 
 * Nel ciclo principale sono previste varie opzioni in base ai tasti premuti dall'utente e nel caso non prema nulla.
 * Vengono aggiornate l'immagine di sfondo, del giocatore e dei cattivi per simulare il movimento e vengono gestite le collisioni.
 * Nel caso si vinca o si perda viene chiesto all'utente se vuole continuare a giocare.
 * Una volta usciti dal ciclo si distruggono tutti i puntatori utilizzati.
 */
 
int main(int argc, char **argv) 
{
	//inizializzazione delle funzioni di allegro
	if (!inizializza())
		return -1;

	//costanti e variabili
	int LDisplay=683;								//larghezza display al momento della creazione
	int puntitmp=0;									//punti temporanei per restituire il massimo ottenuto al gameover
	const int ncattivi=30;							//numero totale di cattivi
	const float FPS = 60.0, giocatoreFPS = 25.0;	//costanti utili per la creazione dei timer timer e giocatoreTimer
	char stringavite[10], stringapunti[10];			//stringhe per la visualizzazione di vite e punti sullo schermo
	bool fine=false;								//variabile per il main loop: quando verrà settata a vero, il gioco terminerà
	bool boolistruzioni=false;						//quando è vera vengono visualizzate le istruzioni
	bool gameover=true;								//serve per decidere se inizializzare o meno le vite del giocatore
	enum stato_t {menu, gioco};						//finchè lo stato è impostato su menu viene visualizzato il menu principale,
													// quando passa a gioco si inizia a giocare

	stato_t stato=menu;			//creo la variabile stato
	sfondo SFONDO;				//creo una struttura per lo sfondo							
	giocatore GIOCATORE;		//creo una struttura per il giocatore
	cattivi CATTIVI[ncattivi];	//creo un array di strutture per i cattivi 
	
	ALLEGRO_DISPLAY *display = al_create_display(LDisplay, HDisplay);	//creo il display
	ALLEGRO_EVENT_QUEUE *CodaEventi = al_create_event_queue();			//creo una coda di eventi per poterli utilizzare
	ALLEGRO_BITMAP *presentazione=al_load_bitmap("menu.jpg");			//carico l'immagine del menu 
	ALLEGRO_BITMAP *istruzioni=al_load_bitmap("istruzioni.jpg"); 		//carico l'immagine delle istruzioni
	ALLEGRO_TIMER *timer = al_create_timer(1.0 / FPS);					//creo un timer generale per il gioco
	ALLEGRO_TIMER *giocatoreTimer = al_create_timer(1.0 / giocatoreFPS);//creo un timer per i movimenti del giocatore
	ALLEGRO_KEYBOARD_STATE StatoTastiera;								//creo un oggetto per capire se è stato premuto qualche tasto
	ALLEGRO_EVENT evento;												//creo un evento per confrontarlo con i timer
	ALLEGRO_FONT *font=al_load_font("MOTO.TTF", 30, 0);   				//carico un font per le scritte
	
	//controllo che la creazione del display e della coda di eventi sia andata a buon fine, altrimenti ritorno errore
	if(!display) 
	{
		al_show_native_message_box(NULL, "Attenzione!", "Errore", "Impossibile creare il display", NULL, ALLEGRO_MESSAGEBOX_ERROR);
		return -1;
	}

	if(!CodaEventi) 
	{
    	al_show_native_message_box(display, "Attenzione!", "Errore", "Impossibile creare la coda", NULL, ALLEGRO_MESSAGEBOX_ERROR);
    	al_destroy_display(display);
    	return -1;
	}
	
	//inizializza sfondo e personaggi
	inizzSfondo(SFONDO);
	inizzGiocatore(GIOCATORE, gameover);
	inizzCattivi(CATTIVI, ncattivi);
	
	//configura alcuni parametri del display, fa iniziare le registrazioni degli eventi nella coda e fa partire i timer
	configura(display, CodaEventi, timer, giocatoreTimer);
	
	//menu e istruzioni
	while (stato!=gioco)
	{			
		al_wait_for_event(CodaEventi, &evento);
		al_get_keyboard_state(&StatoTastiera);
	
		if (al_key_down(&StatoTastiera, ALLEGRO_KEY_ESCAPE) ) 
		{
			fine=true;			
			break;
		}
			
		al_draw_bitmap(presentazione, 0, 0, 0);
		if (boolistruzioni==false) al_flip_display();
		
		else
		{	
			al_clear_to_color(al_map_rgb(0,0,0));	
			al_draw_bitmap(istruzioni, 0, 0, 0);
			al_flip_display();	
				
			if (al_key_down(&StatoTastiera, ALLEGRO_KEY_R)) 
			{
				boolistruzioni=false;
				DBG(cout<<"Fine della visualizzazione istruzioni, torno indietro. boolistruzioni = "<<boolistruzioni<<endl);
			}
		}
					
		if (al_key_down(&StatoTastiera, ALLEGRO_KEY_ENTER))
			stato=gioco;
					
		if (al_key_down(&StatoTastiera, ALLEGRO_KEY_I))
		{
			boolistruzioni=true;
			DBG(cout<<"Visualizzo le istruzioni, boolistruzioni = "<<boolistruzioni<<endl);
		}
	}
	
	//main loop
	while (!fine)
	{
		al_wait_for_event(CodaEventi, &evento);
		al_get_keyboard_state(&StatoTastiera);
		
		//uscita
		if (al_key_down(&StatoTastiera, ALLEGRO_KEY_ESCAPE) )
			break;
			
		else if (evento.type == ALLEGRO_EVENT_TIMER)
		{	
			if (evento.timer.source==timer)
			{	
				//passo alla modalità fullscreen		
				if (al_key_down(&StatoTastiera, ALLEGRO_KEY_F))
				{
					al_set_display_flag(display, ALLEGRO_FULLSCREEN_WINDOW, true);
					LDisplay=1366;
					
					DBG(cout<<"Modalità fullscreen: valore di LDisplay settato a "<<LDisplay<<endl);
				}
				
				//torno alla modalità non fullscreen
				if (al_key_down(&StatoTastiera, ALLEGRO_KEY_N))
				{
					al_set_display_flag(display, ALLEGRO_FULLSCREEN_WINDOW, false);
					LDisplay=683;
					
					DBG(cout<<"Ritorno alla modalità normale: valore di LDisplay settato a "<<LDisplay<<endl);
				}
				
				//il giocatore deve saltare
				if (al_key_down(&StatoTastiera, ALLEGRO_KEY_SPACE))
				{
					GIOCATORE.vely = -10; //velocità di salto
					GIOCATORE.salto=true;
				}
				
				//aggiorno il movimento di sfondo e cattivi
				UpSfondo(SFONDO);
				UpCattivi(CATTIVI, GIOCATORE, ncattivi);
				
				//morto				
				if (GIOCATORE.morto)
				{
					gameover=false;
					puntitmp=espl_giocatore(GIOCATORE, puntitmp);
					inizzSfondo(SFONDO);
					inizzGiocatore(GIOCATORE, gameover);
					inizzCattivi(CATTIVI, ncattivi);
				}	
				
				//salto	
				if (GIOCATORE.salto) 
					saltare(GIOCATORE);
			
				//collisione
				for(int numcattivo=0; numcattivo<ncattivi-1; numcattivo++)
				{				
					if ((GIOCATORE.x + GIOCATORE.larghezza >= CATTIVI[numcattivo].x) && (GIOCATORE.x <= CATTIVI[numcattivo].x + CATTIVI[numcattivo].larghezza))
						if ((GIOCATORE.y + GIOCATORE.altezza >= CATTIVI[numcattivo].y) && (GIOCATORE.y <= CATTIVI[numcattivo].y + CATTIVI[numcattivo].altezza))
						GIOCATORE.morto=true;
				}
   
   				//perso
			   	if (GIOCATORE.vite==0)
			   	{
			   		bool vinto=false;
			   		gameover=true;
			   		finito(StatoTastiera, SFONDO, GIOCATORE, CATTIVI, ncattivi, LDisplay, puntitmp, vinto, fine, gameover);
			   	}
					
				//vinto
				if (GIOCATORE.x + GIOCATORE.larghezza >= CATTIVI[ncattivi-1].x + CATTIVI[ncattivi-1].larghezza)
				{
					bool vinto=true;
					gameover=true;
					finito(StatoTastiera, SFONDO, GIOCATORE, CATTIVI, ncattivi, LDisplay, puntitmp, vinto, fine, gameover);
				}
			}
			
			//movimento spritesheet
			else if (evento.timer.source == giocatoreTimer)
			{
				GIOCATORE.sorgX += GIOCATORE.larghezza; //colonne
				if(GIOCATORE.sorgX>=al_get_bitmap_width(GIOCATORE.immagine)) 
				{
					GIOCATORE.sorgX=0;
					GIOCATORE.sorgY += GIOCATORE.altezza; //righe
					if(GIOCATORE.sorgY>=al_get_bitmap_height(GIOCATORE.immagine))
						GIOCATORE.sorgY=0;
				}
			}		
 		}
		
		//disegno i bitmap
		al_draw_bitmap(SFONDO.immagine, SFONDO.x, SFONDO.y, 0);
		
		if (SFONDO.x + SFONDO.larghezza < LDisplay)
			al_draw_bitmap(SFONDO.immagine, SFONDO.x + SFONDO.larghezza, SFONDO.y, 0);		
			
		for(int numcattivo=0; numcattivo<ncattivi; numcattivo++)
			al_draw_bitmap(CATTIVI[numcattivo].immagine, CATTIVI[numcattivo].x, CATTIVI[numcattivo].y, 0);
			
		if (!GIOCATORE.morto) 
			al_draw_bitmap_region(GIOCATORE.immagine, GIOCATORE.sorgX, GIOCATORE.sorgY, GIOCATORE.larghezza, GIOCATORE.altezza, GIOCATORE.x, GIOCATORE.y, 0); 
		else 
			al_draw_bitmap(GIOCATORE.esplosione, GIOCATORE.x, GIOCATORE.y, 0);
						
		//disegno le stringhe delle vite e dei punti
		sprintf(stringavite, "Vite   %d", GIOCATORE.vite);
		sprintf(stringapunti, "Punti   %d", GIOCATORE.punti);
		al_draw_text(font, al_map_rgb(244, 0, 161), 20, 20, 0, stringavite);	
		al_draw_text(font, al_map_rgb(244, 0, 161), LDisplay-20, 20, ALLEGRO_ALIGN_RIGHT, stringapunti);	
		
		al_flip_display();
		al_clear_to_color(al_map_rgb(0,0,0));		
	}

	//distruzione puntatori
	al_destroy_display(display);
	al_destroy_event_queue(CodaEventi);
	al_destroy_bitmap(presentazione);
	al_destroy_bitmap(istruzioni);
	al_destroy_timer(timer);
	al_destroy_timer(giocatoreTimer);
	al_destroy_font(font);
	al_destroy_bitmap(SFONDO.immagine);
	al_destroy_bitmap(GIOCATORE.immagine);
	al_destroy_bitmap(GIOCATORE.esplosione);
	
	for(int i=0; i<ncattivi; i++)
		al_destroy_bitmap(CATTIVI[i].immagine);
	
	return 0;
}
